#ifndef _MY_INCLUDE_ASYNCEVENTRECEIVER_HH_
#define _MY_INCLUDE_ASYNCEVENTRECEIVER_HH_

#include <sys/epoll.h>
#include <cstddef>
#include <cstring>
#include <utility>

namespace async
{

enum watched_events
{
    WE_IN    = EPOLLIN,    // The associated file is available for read(2) operations.
    WE_OUT   = EPOLLOUT,   // The associated file is available for write(2) operations.
    WE_RDHUP = EPOLLRDHUP, // (since Linux 2.6.17) Stream socket peer closed connection,
                           // or shut down writing half of connection.  (This flag is
                           // especially useful for writing simple code to detect peer
                           // shutdown when using Edge Triggered monitoring.)
    WE_PRI   = EPOLLPRI,   // There is urgent data available for read(2) operations.
    WE_ERR   = EPOLLERR,   // Error condition happened on the associated file descriptor.
                           // epoll_wait(2) will always wait for this event; it is
                           // not necessary to set it in events.
    WE_HUP   = EPOLLHUP    // Hang up happened on the associated file descriptor.
                           // epoll_wait(2) will always wait for this event; it is
                           // not necessary to set it in events.
};

class events
{
public:
    typedef typeof((new struct ::epoll_event)->events) set;

    events():event_(0) { }
    events(set _s):event_(_s) { }
    events(watched_events _s):event_(_s) { }
    events(const events &_src):event_(_src.event_) { }

    operator set()const { return event_; }

    events& operator=(set _s) { event_ = _s; return *this; }
    events& operator=(watched_events _s) { event_ = _s; return *this; }
    events& operator=(const events &_src) { event_ = _src.event_; return *this; }

    events operator+(watched_events _s)const { return event_ | set(_s); }
    events operator+(const events &_src)const { return event_ | _src.event_; }

    events operator|(watched_events _s)const { return event_ | set(_s); }
    events operator|(const events &_src)const { return event_ | _src.event_; }

    bool operator&(watched_events _s)const { return watched_events(event_ & set(_s)) != 0; }
    events operator&(const events &_src)const { return event_ & _src.event_; }

    events& operator+=(watched_events _s) { event_ |= set(_s); return *this; }
    events& operator+=(const events &_src) { event_ |= _src.event_; return *this; }

    events& operator|=(watched_events _s) { event_ |= set(_s); return *this; }
    events& operator|=(const events &_src) { event_ |= _src.event_; return *this; }

    events& operator&=(watched_events _s) { event_ &= set(_s); return *this; }
    events& operator&=(const events &_src) { event_ &= _src.event_; return *this; }
private:
    set event_;
};

typedef int file_descriptor;
typedef file_descriptor event_source;
enum { INVALID_DESCRIPTOR = -1 };

typedef void (*event_receiver_fnc)(event_source, events);
template < typename t > struct event_receiver_m
{
    typedef void (t::*fnc)(event_source, events);
};

class event_receiver
{
public:
    event_receiver(): invoke_(NULL) { }
    template < typename t > event_receiver(std::pair< t*, typename event_receiver_m< t >::fnc > _mfnc)
    :   invoke_(call_mfnc)
    {
        c_.class_method.c = reinterpret_cast< event_receiver* >(_mfnc.first);
        c_.class_method.m = reinterpret_cast< mfnc >(_mfnc.second);
    }
    template < typename t > event_receiver(std::pair< typename event_receiver_m< t >::fnc, t* > _mfnc)
    :   invoke_(call_mfnc)
    {
        c_.class_method.c = reinterpret_cast< event_receiver* >(_mfnc.second);
        c_.class_method.m = reinterpret_cast< mfnc >(_mfnc.first);
    }
    event_receiver(event_receiver_fnc _fnc)
    :   invoke_(call_fnc)
    {
        c_.static_method = _fnc;
    }
    event_receiver(const event_receiver &_src)
    :   invoke_(_src.invoke_)
    {
        std::memcpy(&c_, &_src.c_, sizeof(c_));
    }
    event_receiver& operator=(const event_receiver &_src)
    {
        invoke_ = _src.invoke_;
        std::memcpy(&c_, &_src.c_, sizeof(c_));
        return *this;
    }
    template < typename t > event_receiver& operator=(std::pair< t*, typename event_receiver_m< t >::fnc > _mfnc)
    {
        invoke_ = call_mfnc;
        c_.class_method.c = reinterpret_cast< event_receiver* >(_mfnc.first);
        c_.class_method.m = reinterpret_cast< mfnc >(_mfnc.second);
        return *this;
    }
    template < typename t > event_receiver& operator=(std::pair< typename event_receiver_m< t >::fnc, t* > _mfnc)
    {
        invoke_ = call_mfnc;
        c_.class_method.c = reinterpret_cast< event_receiver* >(_mfnc.second);
        c_.class_method.m = reinterpret_cast< mfnc >(_mfnc.first);
        return *this;
    }
    event_receiver& operator=(event_receiver_fnc _fnc)
    {
        invoke_ = call_fnc;
        c_.static_method = _fnc;
        return *this;
    }
    void operator()(event_source _p0, events _p1) { invoke_(this, _p0, _p1); }
private:
    typedef event_receiver_m< event_receiver >::fnc mfnc;
    struct cm
    {
        event_receiver *c;
        mfnc m;
    };
    union call
    {
        event_receiver_fnc static_method;
        cm class_method;
    } c_;
    void (*invoke_)(event_receiver*, event_source, events);
    static void call_fnc(event_receiver *_o, event_source _p0, events _p1) { _o->c_.static_method(_p0, _p1); }
    static void call_mfnc(event_receiver *_o, event_source _p0, events _p1) { (_o->c_.class_method.c->*_o->c_.class_method.m)(_p0, _p1); }
};

}

#endif//_MY_INCLUDE_ASYNCEVENTRECEIVER_HH_
